package uniandes.dermatologia.resources;

import java.io.ObjectInputStream;
import java.util.ArrayList;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import rajawali.BaseObject3D;
import rajawali.SerializedObject3D;
import rajawali.animation.Animation3D;
import rajawali.animation.RotateAnimation3D;
import rajawali.lights.DirectionalLight;
import rajawali.materials.DiffuseMaterial;
import rajawali.math.Number3D;
import rajawali.math.Number3D.Axis;
import rajawali.math.Quaternion;
import rajawali.renderer.RajawaliRenderer;
import rajawali.util.ObjectColorPicker;
import rajawali.util.OnObjectPickedListener;
import uniandes.dermatologia.BodyActivity;
import uniandes.dermatologia.R;
import uniandes.dermatologia.training.activities.BodyCaseActivity;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.animation.AccelerateDecelerateInterpolator;

public class Render extends RajawaliRenderer implements OnObjectPickedListener {

	private final static String TAG = "Render";

	private BaseObject3D[] modelos = new BaseObject3D[21];
	
	private ArrayList<BaseObject3D> toColor;

	public BaseObject3D[] getModelos() {
		return modelos;
	}

	public void setModelos(BaseObject3D[] modelos) {
		this.modelos = modelos;
	}

	private boolean[] estaSeleccionadoModelo = new boolean[21];

	private DirectionalLight mLight;
	private float recentCameraDistance;
	private float cameraDistance = -20f;
	// private float cameraY;
	private ObjectColorPicker mPicker;

	private Quaternion mDragRotation = null;
	private Animation3D animDerecha;

	private Animation3D animIzquierda;

	public Render(Context context) {
		super(context);
		setFrameRate(60);
	}

	
	protected void initScene() {
		setBackgroundColor(0);

		mPicker = new ObjectColorPicker(this);
		mPicker.setOnObjectPickedListener(this);

		mCamera.setPosition(0f, 5f, 0f);

		setCameraDistance(1f);
		mLight = new DirectionalLight(1f, 0.2f, 1.0f); // set the direction
		mLight.setColor(1.0f, 1.0f, 1.0f);
		mLight.setPower(0.7f);

		// SE USA EL ABDOMEN COMO CENTRO Y PADRE DE EL MODELO PARA HACER LAS
		// ROTACIONES SOBRE ESTE
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.abdomen));
			modelos[Constants.ABDOMEN] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			modelos[Constants.ABDOMEN].setMaterial(new DiffuseMaterial());
			modelos[Constants.ABDOMEN].getMaterial().setUseColor(true);
			modelos[Constants.ABDOMEN].setColor(Color.WHITE);
			modelos[Constants.ABDOMEN].setLight(mLight);
			modelos[Constants.ABDOMEN].setName(0 + "");
			modelos[Constants.ABDOMEN].setRotation(0, 180, 0);
			modelos[Constants.ABDOMEN].setScale(15f, 15f, 15f);
			mPicker.registerObject(modelos[Constants.ABDOMEN]);
			addChild(modelos[Constants.ABDOMEN]);

			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.anogenital));
			modelos[Constants.ANOGENITAL] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.arm));
			modelos[Constants.ARM] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.back));
			modelos[Constants.BACK] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.buttock));
			modelos[Constants.BUTTOCK] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.chest));
			modelos[Constants.CHEST] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.dorsal_foot));
			modelos[Constants.DORSAL_FOOT] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.dorsal_hand));
			modelos[Constants.DORSAL_HAND] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.ear));
			modelos[Constants.EAR] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.face));
			modelos[Constants.FACE] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.finger));
			modelos[Constants.FINGER] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.nail));
			modelos[Constants.NAIL] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.finger_web));
			modelos[Constants.FINGER_WEB] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.head));
			modelos[Constants.HEAD] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.leg));
			modelos[Constants.LEG] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.neck));
			modelos[Constants.NECK] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.palm));
			modelos[Constants.PALM] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.scalp));
			modelos[Constants.SCALP] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.sole));
			modelos[Constants.SOLE] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.toe));
			modelos[Constants.TOE] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			ObjectInputStream ois;
			ois = new ObjectInputStream(mContext.getResources()
					.openRawResource(R.raw.toe_nail));
			modelos[Constants.TOE_NAIL] = new BaseObject3D(
					(SerializedObject3D) ois.readObject());
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

		for (int i = 1; i < modelos.length; i++) {
			mPicker.registerObject(modelos[i]);
			Log.d("Render", "Se esta procesando el modelo: " + i);
			modelos[i].setMaterial(new DiffuseMaterial());
			modelos[i].getMaterial().setUseColor(true);
			modelos[i].setColor(Color.WHITE);
			modelos[i].setLight(mLight);
			modelos[i].setName(i + "");
			modelos[Constants.ABDOMEN].addChild(modelos[i]);

		}

		animDerecha = new RotateAnimation3D(Axis.Y, -360);
		animDerecha.setDuration(3000);
		animDerecha.setRepeatCount(Animation3D.INFINITE);
		animDerecha.setInterpolator(new AccelerateDecelerateInterpolator());
		animDerecha.setTransformable3D(modelos[Constants.ABDOMEN]);

		animIzquierda = new RotateAnimation3D(Axis.Y, 360);
		animIzquierda.setDuration(3000);
		animIzquierda.setRepeatCount(Animation3D.INFINITE);
		animIzquierda.setInterpolator(new AccelerateDecelerateInterpolator());
		animIzquierda.setTransformable3D(modelos[Constants.ABDOMEN]);
		toColor = new ArrayList<BaseObject3D>();
		Log.d("Render", "Se cargo todo bien");
		storeRotation();

	}

	public void iniciarAnimacionDerecha() {

		animDerecha.start();
	}

	public void detenerAnimacionDerecha() {
		animDerecha.cancel();

	}

	public void iniciarAnimacionIzquierda() {

		animIzquierda.start();
	}

	public void detenerAnimacionIzquierda() {
		animIzquierda.cancel();

	}

	// public void drag(float x, float y) {
	// // Rotates a short distance
	//
	// Log.d(TAG, "drag x: " + x + " y: " + y);
	// // Determine how far to rotate and in which direction
	//
	// float rotAng = magnitudeOfRotation(-x, 0) * -0.5f;
	// Number3D axis = perpendicularAxis(-x, 0);
	//
	// // If our objects have an existing rotation, transform
	// // the axis of rotation
	//
	// if (mDragRotation != null) {
	// Matrix4 mat = mDragRotation.toRotationMatrix().inverse();
	// axis = mat.transform(axis);
	// }
	// axis.normalize();
	//
	// // Get the new rotation as a quaternion
	//
	// Quaternion rot = new Quaternion();
	// rot.fromAngleAxis(rotAng, axis);
	//
	// // Apply any existing rotation to it
	//
	// if (mDragRotation != null)
	// rot.multiply(mDragRotation);
	//
	//
	// modelos[ABDOMEN].setPosition(rot.y, rot.x, rot.z);
	//
	// }
	public void drag(float x, float y) {

		float currentX = modelos[Constants.ABDOMEN].getX();
		float currentY = modelos[Constants.ABDOMEN].getY();

		float moveY = (y / 5000f) * (-1);
		moveY += currentY;
		Log.d(TAG, "el currentX del drag es: " + currentX + " el currentY:"
				+ currentY);
		Log.d(TAG, "el x del drag es: " + x + " el y:" + y);

		modelos[Constants.ABDOMEN].setPosition(currentX, moveY, 0);

	}

	private float magnitudeOfRotation(float x, float y) {
		return new Number3D(x, y, 0).length();
	}

	private Number3D perpendicularAxis(float x, float y) {
		// Uses a fairly unsophisticated approach to generating
		// a perpendicular vector

		if (y == 0)
			return new Number3D(y, -x, 0);
		else
			return new Number3D(-y, x, 0);
	}

	public void pinchOrDragFinished() {
		cameraDistance = recentCameraDistance;

		storeRotation();
	}

	private void setCameraDistance(float scale) {
		// Set our camera distance

		float tmpDistance = cameraDistance / scale;
		if (tmpDistance >= -20f && tmpDistance <= -5f) {

			recentCameraDistance = tmpDistance;
			mCamera.setZ(recentCameraDistance);
		}
		Log.d(TAG, "setCameraDistance con cameraDistance: " + cameraDistance
				+ " y recentCameraDistance de: " + recentCameraDistance
				+ " scale:" + scale);
	}

	public void pinch(float scale) {
		// Zooms the view

		setCameraDistance(scale);
	}

	private void storeRotation() {

		mDragRotation = modelos[Constants.ABDOMEN].getOrientation();
	}

	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		super.onSurfaceCreated(gl, config);
		try {
			((BodyActivity) mContext).hideLoader();
		} catch (ClassCastException e) {
			((BodyCaseActivity) mContext).hideLoader();
		}
		
	}

	/*
	 * (non-Javadoc)
	 * @see rajawali.util.OnObjectPickedListener#onObjectPicked(rajawali.BaseObject3D)
	 */
	@Override
	public void onObjectPicked(BaseObject3D object) {

		Log.d(TAG, "onObjectPicked: " + object.getName());
		int numero = Integer.parseInt(object.getName());
		if (estaSeleccionadoModelo[numero]) {
			object.setColor(Color.WHITE);
			estaSeleccionadoModelo[numero] = false;
		} else {

			object.setColor(Color.RED);
			estaSeleccionadoModelo[numero] = true;
		}
	}

	public boolean[] getEstaSeleccionadoModelo() {
		return estaSeleccionadoModelo;
	}

	public void setEstaSeleccionadoModelo(boolean[] estaSeleccionadoModelo) {
		this.estaSeleccionadoModelo = estaSeleccionadoModelo;
	}
	
	public void pickObjects()
	{
		boolean b;
		BaseObject3D object3d;
		int name;
		if(estaSeleccionadoModelo != null)
		{
			for(int i = 0; i < estaSeleccionadoModelo.length; i ++)
			{
				b = estaSeleccionadoModelo[i];				
				if(b)
				{
					for (int j = 0; j < modelos.length; j++) {
						
						object3d = modelos[j];
						name = Integer.parseInt(object3d.getName()); 
						if(i == name)
						{
							toColor.add(object3d);
						}
					}
				}
			}
		}
	}

	public void singleTap(float x, float y) {
		mPicker.getObjectAt(x, y);
	}

	public void doubleTap(float x, float y) {

		mPicker.getObjectAt(x, y);
	}
	
	@Override
	public void onDrawFrame(GL10 glUnused) {
		super.onDrawFrame(glUnused);
		for(BaseObject3D object3d : toColor)
		{
			object3d.setColor(Color.RED);
		}
	}
}
